//Class that represents a 64-bit word
//Internaly bit0 is the least significant bit 
//and bit63 is the most significant bit
// 
//The word can be initiated through different constructors
//For output conversions to hexadecimal or binary string are supported
#ifndef w64
#define w64

#include <iostream>
#include <cstdint>
#include <string>

class word64 {
 private:
  //the 64-bit word
  uint64_t w;
  
 public:
  //initiate w to 0 in default constructor
  word64(){w=0;}

  //initiate w to the value given by the caller
  //the caller must make sure that the integer can fit in a 64-bit word
  word64(unsigned long long word){w=word;}

  //initiate w to the value given by the word64 source
  word64(const word64& source){w=source.w;}

  //initiate w to the value given by the input string
  //The string hould be encoded with LSB first
  //It is also assumed that the length of the string is not longer than 64-bits
  //But shorter is ok
  word64(std::string inputString); 

  //check for equality, returns true if the value of the words are equal
  inline bool equal(const word64& source){return w==source.w;}

  //assign the word to a value given by another word64
  inline void assign(const word64& word){
    w=word.w;
  }

  //assign the word to a value given by a unsigned long
  inline void assign(unsigned long long word){
    w=word;
  }

  //assign w to the value given by the input string
  //The string hould be encoded with LSB first
  //It is also assumed that the length of the string is not longer than 64-bits
  //But shorter is ok
  void assign(std::string inputString);  

  //return the value of the bit in position k
  inline int getBit(int k){
    return (w&(1uLL<<k))==0?0:1;
  }

  //set the bit in position k to 1 
  inline void setBit(int k){
    w|=(1uLL<<k);
  }

  //set bit in position k to 0
  inline void clearBit(int k){
    w&=(~(1uLL<<k));
  }

  //set bit in position k to val
  //it is assumed but never tested that 
  //val is 0 or 1
  inline void setVal(int k,int val){
    if(val==0)
      clearBit(k);
    if(val==1)
      setBit(k);
  }
 
  //return the word encoded as a string
  //binary representation with the least significant bit first
  std::string toBinStringLSB();

  //return the word encoded as a string
  //binary representation with the most significant bit first
  std::string toBinStringMSB();

  //return the word encoded as a string
  //hexadecimal representation with the  most significant bit first
  std::string toHexStringMSB();

  //return the word encoded as a string
  //hexadecimal representation with the  least significant bit first
  std::string toHexStringLSB();

  //logical operator and
  inline void andOp(const word64& source){
    w&=source.w;
  }

  //logical operator or
  inline void orOp(const word64& source){
    w|=source.w;
  }

  //logical operator xor
  inline void xorOp(const word64& source){
    w^=source.w;
  }

  //logical operator not
  inline void notOp(){
    w=~w;
  }

  //addition mod 2^64
  inline void add(const word64& source){
    w+=source.w;
  }

  //subtraction mod 2^64
  //w=w-source
  inline void sub(const word64& source){
    w-=source.w;
  }

  //left shift (If we consider the word MSB first)
  //shift k steps
  inline void lshift(int k){
    w<<=k;
  }

  //right shift (If we consider the word MSB first)
  //shift k steps
  inline void rshift(int k){
    w>>=k;
  }

  //left circular shift k steps (MSB first)
  inline void cshift(int k){
    w=((w<<k)|(w>>(64-k)));
  }

  //calculates the number of 1 in the word
  int weight();

};

#endif
